package ogs.lib.mina.filter.codec
{
	import flash.utils.ByteArray;
	import flash.utils.getQualifiedClassName;
	
	import org.osflash.thunderbolt.Logger;
	
	import ogs.lib.mina.core.filterchain.IoFilterAdapter;
	import ogs.lib.mina.core.filterchain.IoFilterChain;
	import ogs.lib.mina.core.filterchain.NextFilter;
	
	public class ProtocolCodecFilter extends IoFilterAdapter
	{
		private var factory:ProtocolCodecFactory;
		private var enout:ProtocolEncoderOutput;
		private var deout:ProtocolDecoderOutput;
		
		public function ProtocolCodecFilter(factory:ProtocolCodecFactory)
		{
			if (factory == null) {
				throw new ArgumentError("factory");
			}
			
			this.factory = factory;
		}
		

		override public function onPreAdd(parent:IoFilterChain, name:String, nextFilter:NextFilter):void
		{
			if (parent.contains(this)) {
				throw new ArgumentError(
					"You can't add the same filter instance more than once.  Create another instance and add it.");
			}
		}
		
		override public function onPostRemove(parent:IoFilterChain, name:String, nextFilter:NextFilter):void
		{
			disposeCodec();
		}
		
		override public function messageReceived(nextFilter:NextFilter, message:Object):void
		{
			Logger.debug( "Processing a MESSAGE_RECEIVED");
			
			if (!(message is ByteArray)) {
				nextFilter.messageReceived(message);
				return;
			}
			
			var indata:ByteArray = ByteArray(message);
			var decoder:ProtocolDecoder = factory.getDecoder();
			var decoderOut:ProtocolDecoderOutput = getDecoderOut(nextFilter);
			while (indata.bytesAvailable > 0) {
				try {
					decoder.decode(indata, decoderOut);
					decoderOut.flush(nextFilter);
				} catch (t:Error) {
					decoderOut.flush(nextFilter);
					nextFilter.exceptionCaught(t);
				}
			}
		}
		
		override public function filterWrite(nextFilter:NextFilter, message:Object):void
		{
			var encoder:ProtocolEncoder = factory.getEncoder();
			var encoderOut:ProtocolEncoderOutput = getEncoderOut(nextFilter);
			
			if (encoder == null) {
				throw new Error("The encoder is null for the session ");
			}
			
			if (encoderOut == null) {
				throw new Error("The encoderOut is null for the session ");
			}
			
			try {
				encoder.encode(message, encoderOut);
				encoderOut.flush();
			} catch (e:Error) {
				Logger.error("Encoder Error", e.message);
			}
		}

		private function disposeCodec():void
		{
			disposeEncoder();
			disposeDecoder();
			disposeDecoderOut();
		}
		
		private function disposeEncoder():void
		{
			var encoder:ProtocolEncoder = factory.getEncoder();
			if (encoder == null) {
				return;
			}
			
			try {
				encoder.dispose();
			} catch (e:Error) {
				Logger.warn(
					"Failed to dispose: " + getQualifiedClassName(encoder) + " (" + encoder + ')');
			}
		}
		
		private function disposeDecoder():void
		{
			var decoder:ProtocolDecoder = factory.getDecoder();
			if (decoder == null) {
				return;
			}
			
			try {
				decoder.dispose();
			} catch (e:Error) {
				Logger.warn(
					"Failed to dispose: " + getQualifiedClassName(decoder) + " (" + decoder + ')');
			}
		}
		
		private function disposeDecoderOut():void
		{
			// do nothing
		}
		
		private function getEncoderOut(nextFilter:NextFilter):ProtocolEncoderOutput
		{
			if (enout == null)
				enout = new ProtocolEncoderOutputImpl(nextFilter);
			
			return enout;
		}

		private function getDecoderOut(nextFilter:NextFilter):ProtocolDecoderOutput
		{
			if (deout == null)
				deout = new ProtocolDecoderOutputImpl();
			
			return deout;
		}
		
	}
}